#include "HCNG.h"
#include "QString"
#include "QDebug"
#include <QMessageBox>


/****************************************
@function:构造函数
@param:void
@return:void
@note:
****************************************/
HCNG::HCNG()
{
    frame_init(&parameter.frame, 1, dFrame_LengthMax);
    frame_identify_add(&parameter.frame, dFrame_IdentifyA);

    parameter.serialPort = new QSerialPort();

    connect(this,&HCNG::Serial_Connected_signal,this,&HCNG::Serial_Connected_slot);
    connect(this,&HCNG::Serial_DisConnected_signal,this,&HCNG::Serial_DisConnected_slot);
    connect(this,&HCNG::Serial_TransferData_signal,this,&HCNG::Serial_TransferData_slot);

    connect(parameter.serialPort,QOverload<QSerialPort::SerialPortError>::of(&QSerialPort::errorOccurred),this,&HCNG::Serial_Error_slot);
    connect(parameter.serialPort,&QSerialPort::readyRead,this,&HCNG::Serial_ReadyRead_slot);

    parameter.timer.setTimerType(Qt::PreciseTimer);// 设置定时器类型为精确定时器
     connect(&parameter.timer, SIGNAL(timeout()), this, SLOT(Timer_slot()));
}

/****************************************
@function:析构函数
@param:void
@return:void
@note:
****************************************/
HCNG::~HCNG()
{
    delete parameter.serialPort;
}
/****************************************
@function:串口连接成功槽函数
@param:void
@return:void
@note:
****************************************/
void HCNG::Serial_Connected_slot(QString baudRate,QString port)
{

    if((baudRate.isEmpty()) || (port.isEmpty())){emit MessageHint_signal("端口未选择"); return;}
    if(parameter.serialPort->isOpen()){QMessageBox::information(nullptr,"提示","串口已打开!");return;}

    QSerialPort::BaudRate baudrate = QSerialPort::Baud4800;//波特率

    if(baudRate == QString("4800")){baudrate = QSerialPort::Baud4800;}
    else if(baudRate == QString("9600")){baudrate = QSerialPort::Baud9600;}
    else if(baudRate == QString("115200")){baudrate = QSerialPort::Baud115200;}
    else{emit MessageHint_signal("波特率无效！");}

    parameter.serialPort->setPortName(port);
    parameter.serialPort->setBaudRate(baudrate);
    parameter.serialPort->setDataBits(QSerialPort::Data8);
    parameter.serialPort->setStopBits(QSerialPort::OneStop);
    parameter.serialPort->setParity(QSerialPort::NoParity);
    parameter.serialPort->setFlowControl(QSerialPort::NoFlowControl);

    if(parameter.serialPort->open(QIODevice::ReadWrite) == true)
    {
        emit MessageHint_signal("串口打开成功");
        //QMessageBox::information(nullptr,"提示","串口打开成功");
    }
    else{QMessageBox::information(nullptr,"提示","串口不存在或被其他设备占用！");}
    return;
}
/****************************************
@function:串口关闭槽函数
@param:void
@return:void
@note:
****************************************/
void HCNG::Serial_DisConnected_slot(void)
{
    if(!parameter.serialPort->isOpen())
    {
        //emit MessageHint_signal("串口未打开，请先打开串口！");
        return;
    }
    parameter.serialPort->close();
    emit MessageHint_signal("串口已关闭");
    //QMessageBox::information(nullptr,"提示","串口已关闭");
}
/****************************************
@function:串口异常关闭槽函数
@param:void
@return:void
@note:
****************************************/
void HCNG::Serial_Error_slot(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError ||error == QSerialPort::PermissionError ||
        error == QSerialPort::DeviceNotFoundError ||error == QSerialPort::NotOpenError || error == QSerialPort::OpenError)//处理串口的权限错误或设备断开错误
    {
        Serial_DisConnected_slot();//关闭设备
        emit MessageHint_signal(QString("串口异常") + parameter.serialPort->errorString());
    }
}
/****************************************
@function:串口发送数据
@param:void
@return:void
@note:
****************************************/
void HCNG::Serial_TransferData_slot(quint8 *data,quint32 length)
{
    if((data == NULL) || (length == 0))return;
    if (!parameter.serialPort->isOpen()) return;
    parameter.serialPort->write((const char *)data,length);
    parameter.serialPort->flush();
}
/****************************************
@function:接收数据识别帧格式
@param:data:数据;length:数据长度
@return:void
@note:
****************************************/
void HCNG::Protocol_Find(quint8 *data,quint32 length)
{
    uint8_t sum = 0;
    if((data == NULL) || (length == 0))return;
    for(quint32 i = 0;i < length;i++)
    {

        if(frame_find(&parameter.frame,data[i]) < 0)continue;
        sum = frame_ChecksumGet(parameter.frame.rx.pbuf,(dFrame_FormatSize(parameter.frame.data_rx.identify) + parameter.frame.data_rx.size - 1));//最后一个字节为校验和
        if(sum != parameter.frame.data_rx.pbuf[parameter.frame.data_rx.size - 1])continue;//校验错误
        //frame_data_qDebug(parameter.frame.data_rx);//有效帧打印输出
        emit FrameCommandReceive_signal(parameter.frame.data_rx.identify,parameter.frame.data_rx.pbuf,parameter.frame.data_rx.size);
        parameter.RxNum++;
    }
}
/****************************************
@function:定时器回调槽函数
@param:void
@return:void
@note:
****************************************/
void HCNG::Timer_slot(void)
{
    parameter.TxNum++;
    static quint16 ms = 0;
    if(ms++ >= 100)
    {
        ms = 0;
        emit MessageHint_signal(QString("Tx/Rx=") + QString::number(parameter.TxNum) + QString(" / ") + QString::number(parameter.RxNum));
    }
}
/****************************************
@function:串口读取接收数据槽函数
@param:void
@return:void
@note:
****************************************/
void HCNG::Serial_ReadyRead_slot(void)
{
    QByteArray data = parameter.serialPort->readAll();
    quint8 *pbuf = (quint8 *)data.data();
    Protocol_Find(pbuf,data.size());
}

/****************************************
@function:帧接收信息打印
@param:data--接收到的帧数据
@return:void
@note:
@remark:定义一个字符串组装一帧并打印"Identify=0x81 size=4 data{0xE0,0x01,0x00,0xE4}"
****************************************/
void HCNG::frame_data_qDebug(Frame_Data_StructDef data)
{
    uint32_t i = 0;

    QString str = QString("Identify=0x")+QString("%1").arg(data.identify,2,16,QLatin1Char('0')).toUpper()\
                  +QString(" size=")+QString("%2").arg(data.size);
    for (i = 0; i < data.size; i++)
    {
        if (i == 0)str += QString(" data{0x")+QString("%1").arg(data.pbuf[i],2,16,QLatin1Char('0')).toUpper();
        else str += QString(",0x")+QString("%1").arg(data.pbuf[i],2,16,QLatin1Char('0')).toUpper();
    }
    if (i != 0)str+=QString("}");
    qDebug()<<str;
}
/****************************************
@function:发送文件内容
@param:
    FileName:文件名
    FolderPath:文件地址
@return:0--成功；-1--失败
@note:
****************************************/
uint8_t  HCNG::RunFileTransmit_slot()
{
    QFile file;
    QTextStream file_stream;
    uint32_t line = 0;
    QString line_str;
    QByteArray line_buf;
    char val_str[20000] = {0};
    const char *pbuf = NULL;
    FileFormatContent_User_StructDef FileFormat;


    file.setFileName("text.csv");
    if(file.open(QIODevice::ReadWrite | QIODevice::Text))
    {
        file_stream.setDevice(&file);
#if dFilemode
        file_stream.setEncoding(QStringConverter::Utf8);//设置文本流编码，确保中文等非ASCII字符能正确写入
#else
        file_stream.setCodec("UTF-8");
#endif
        line = 0;
        while(!file_stream.atEnd())
        {
            line_str = file_stream.readLine();
            if(line_str.isEmpty())break;
            line_buf = line_str.toUtf8();
            pbuf = line_buf.data();
            memset(val_str,0,sizeof(val_str));
            if(line++ == 0){continue;}

            sscanf_s(pbuf,"%hu,%f,%f,%f,%f\n",\
                    &FileFormat.SerialNumber,\
                    &FileFormat.temperature,\
                    &FileFormat.concentration,\
                    &FileFormat.VoltageTemperature,\
                    &FileFormat.VoltageHydrogen);

            qDebug()<<FileFormat.SerialNumber
                     <<FileFormat.temperature
                     <<FileFormat.concentration
                     <<FileFormat.VoltageTemperature
                     <<FileFormat.VoltageHydrogen;
        }
        file.close();
    }
    else{emit MessageHint_signal("csv文件不存在！");return -1;}
    return 0;
}
/****************************************
@function:命令
@param:
    address:设备地址
    cmd:命令类型
    code:命令编码
@return:false=参数错误，true=参数正确
@note:
****************************************/
bool HCNG::FrameCommand_ValueCheck(quint8 address,quint8 cmd,quint8 code)
{
    uint8_t CodeMax = 0;
    QString error = QString("参数错误:命令类型错误:设备地址=%1 命令值=%2 编码值=%3").arg(address).arg(cmd).arg(code);

    switch(cmd)
    {
    case eCommandType_Error:CodeMax = eCodeType_Error_Num;break;
    case eCommandType_Device:CodeMax = eCodeType_Device_Num;break;
    case eCommandType_Debug:CodeMax = eCodeType_Debug_Num;break;
    default:{emit MessageHint_signal(error);return false;}
    }

    if((code >= CodeMax) || (address > eDeviceAddress_Broadcast)){emit MessageHint_signal(error);return false;}
    return true;
}

/****************************************
@function:帧命令发送:仅支持无携带数据的命令
@param:
    address:设备地址
    cmd:命令类型
    code:命令编码
@return:假-发送失败，真-发送成功
@note:
****************************************/
bool HCNG::FrameCommandSend_General(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code)
{
    uint16_t pos = 0;
    uint8_t *pbuf = parameter.FrameSendBufferA;

    if(address >= eDeviceAddress_Master)
    {
        emit MessageHint_signal("设置地址不得大于253或目标地址有误!");return false;
    }
    if(!FrameCommand_ValueCheck(address,cmd,code))return false;
    *(pbuf + pos++) = dFrame_IdentifyA;
    *(pbuf + pos++) = dFrame_IdentifyA_Invert;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = eDeviceAddress_Master;
    *(pbuf + pos++) = address;
    *(pbuf + pos++) = (uint8_t)cmd;
    *(pbuf + pos++) = code;

    pos++;
    *(pbuf + 2) = dDataLength_MSB(pos-4);
    *(pbuf + 3) = dDataLength_LSB(pos-4);
    *(pbuf + pos-1) = frame_ChecksumGet(pbuf,pos-1);

    emit Serial_TransferData_signal(pbuf,pos);
    return true;
}
/****************************************
@function:帧命令发送:系统仅恢复出厂设置
@param:
    address:设备地址
    cmd:命令类型
    code:命令编码
@return:假-发送失败，真-发送成功
@note:
****************************************/
bool HCNG::FrameCommandSend_SystemRestore(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code)
{
    uint16_t pos = 0;
    uint8_t *pbuf = parameter.FrameSendBufferA;

    if(!FrameCommand_ValueCheck(address,cmd,code))return false;
    *(pbuf + pos++) = dFrame_IdentifyA;
    *(pbuf + pos++) = dFrame_IdentifyA_Invert;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = eDeviceAddress_Master;
    *(pbuf + pos++) = address;
    *(pbuf + pos++) = (uint8_t)cmd;
    *(pbuf + pos++) = code;

    pos++;
    *(pbuf + 2) = dDataLength_MSB(pos-4);
    *(pbuf + 3) = dDataLength_LSB(pos-4);
    *(pbuf + pos-1) = frame_ChecksumGet(pbuf,pos-1);

    emit Serial_TransferData_signal(pbuf,pos);
    return true;
}
/****************************************************
@function:发送通用命令(只携带一个命令内容值)
@param:
    address:设备地址
    cmd:命令类型
    code:命令编码
    value:值
@return:假-发送失败，真-发送成功
@note:
****************************************************/
bool HCNG::FrameCommandSend_General_OneValue(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,uint8_t value)
{
    uint16_t pos = 0;
    uint8_t *pbuf = parameter.FrameSendBufferA;
    if((address >= eDeviceAddress_Master) || (value >=eDeviceAddress_Master))
    {
        emit MessageHint_signal("设置地址不得大于253或目标地址有误!");return false;
    }
    if(!FrameCommand_ValueCheck(address,cmd,code))return false;
    *(pbuf + pos++) = dFrame_IdentifyA;
    *(pbuf + pos++) = dFrame_IdentifyA_Invert;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = eDeviceAddress_Master;
    *(pbuf + pos++) = address;
    *(pbuf + pos++) = (uint8_t)cmd;
    *(pbuf + pos++) = code;
    *(pbuf + pos++) = value;

    pos++;
    *(pbuf + 2) = dDataLength_MSB(pos-4);
    *(pbuf + 3) = dDataLength_LSB(pos-4);
    *(pbuf + pos-1) = frame_ChecksumGet(pbuf,pos-1);

    emit Serial_TransferData_signal(pbuf,pos);
    return true;
}

/****************************************************
@function:发送通用命令(只携带一个命令内容值)
@param:
    address:设备地址
    cmd:命令类型
    code:命令编码
    state:bool--(假:不使用;真:使用)
@return:假-发送失败，真-发送成功
@note:
****************************************************/
bool HCNG::FrameCommandSend_General_OneState(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,bool State)
{
    uint16_t pos = 0;
    uint8_t *pbuf = parameter.FrameSendBufferA;
    if(address >= eDeviceAddress_Master)
    {
        emit MessageHint_signal("设置地址不得大于253或目标地址有误!");return false;
    }
    if(!FrameCommand_ValueCheck(address,cmd,code))return false;
    *(pbuf + pos++) = dFrame_IdentifyA;
    *(pbuf + pos++) = dFrame_IdentifyA_Invert;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = eDeviceAddress_Master;
    *(pbuf + pos++) = address;
    *(pbuf + pos++) = (uint8_t)cmd;
    *(pbuf + pos++) = code;
    *(pbuf + pos++) = State;//(假:不使用;真:使用)

    pos++;
    *(pbuf + 2) = dDataLength_MSB(pos-4);
    *(pbuf + 3) = dDataLength_LSB(pos-4);
    *(pbuf + pos-1) = frame_ChecksumGet(pbuf,pos-1);

    emit Serial_TransferData_signal(pbuf,pos);
    return true;
}
/****************************************************
@function:发送通用命令(只携带一个命令内容值)
@param:
    address:设备地址
    cmd:命令类型
    code:命令编码
    value:值
@return:假-发送失败，真-发送成功
@note:
****************************************************/
bool HCNG::FrameCommandSend_General_FileObject(quint8 address,Protocol_CommandType_EnumDef cmd,quint8 code,Memory_FileObject_EnumDef Object)
{
    uint16_t pos = 0;
    uint8_t *pbuf = parameter.FrameSendBufferA;
    if(address >= eDeviceAddress_Master)
    {
        emit MessageHint_signal("设置地址不得大于253或目标地址有误!");return false;
    }
    if(!FrameCommand_ValueCheck(address,cmd,code))return false;
    *(pbuf + pos++) = dFrame_IdentifyA;
    *(pbuf + pos++) = dFrame_IdentifyA_Invert;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = 0;
    *(pbuf + pos++) = eDeviceAddress_Master;
    *(pbuf + pos++) = address;
    *(pbuf + pos++) = (uint8_t)cmd;
    *(pbuf + pos++) = code;
    *(pbuf + pos++) = Object;

    pos++;
    *(pbuf + 2) = dDataLength_MSB(pos-4);
    *(pbuf + 3) = dDataLength_LSB(pos-4);
    *(pbuf + pos-1) = frame_ChecksumGet(pbuf,pos-1);

    emit Serial_TransferData_signal(pbuf,pos);
    return true;
}

/****************************************************
@function:发送文件user0
@param:
    address:设备地址
    buf：发送缓冲区
    BufferSize:缓冲区字节数
    index:行数据索引值
    ResidueNumber:剩余发送行数,发送成功后会自动递减已发送行数
    data:行数据
@return:假-发送失败，真-发送成功
@note:
****************************************************/
bool HCNG::Protocol_CommandSend_FileTransmit_DataUser0(quint8 address,quint8 *buf,quint32 BufferSize,quint16 &index,quint16 &ResidueNumber,FileFormatContent_User_StructDef *data)
{
    if((buf == NULL) || (BufferSize == 0) || (ResidueNumber == 0) || (data == NULL))return false;
    if(address >= eDeviceAddress_Master)
    {
        emit MessageHint_signal("设置地址不得大于253或目标地址有误!");return false;
    }
    quint32 pos = 0;
    quint16 ContentSize = 0,FrameLineNumberMax = 0;
    quint8 FrameLineNumber = 0;

    ContentSize = sizeof(FileFormatContent_User_StructDef);
    FrameLineNumberMax = (BufferSize - 14)/ContentSize;//帧最大携带数据长度
    FrameLineNumber = (ResidueNumber > FrameLineNumberMax)?FrameLineNumberMax:ResidueNumber;//计算帧发送多少行数据
    pos = 0;
    *(buf + pos++) = dFrame_IdentifyA;
    *(buf + pos++) = dFrame_IdentifyA_Invert;
    *(buf + pos++) = 0;
    *(buf + pos++) = 0;

    *(buf + pos++) = eDeviceAddress_Master;//发送设备地址
    *(buf + pos++) = address;//接收设备地址
    *(buf + pos++) = eCommandType_Device;
    *(buf + pos++) = eCodeType_Device_FileTransmit_Data;

    *(buf + pos++) = eMemory_FileObject_User0;//文件对象
    *(buf + pos++) = dDataLength_MSB(index);//行索引值,起始值0
    *(buf + pos++) = dDataLength_LSB(index);
    *(buf + pos++) = dDataLength_MSB(FrameLineNumber);//帧携带行数
    *(buf + pos++) = dDataLength_LSB(FrameLineNumber);

    //复制行数据
    memcpy((void *)(buf + pos),(uint8_t *)data,FrameLineNumber*ContentSize);
    pos += FrameLineNumber * ContentSize;
    *(buf + 2) = dDataLength_MSB(pos-4+1);//帧数据字节数高8位
    *(buf + 3) = dDataLength_LSB(pos-4+1);//帧数据字节数低8位
    *(buf + pos++) = frame_ChecksumGet(buf,pos);
    emit Serial_TransferData_signal(buf,pos);

    ResidueNumber -= FrameLineNumber;
    index += FrameLineNumber;
    return true;
}

/****************************************************
@function:发送文件结束帧
@param:
    address:设备地址
    buf：发送缓冲区
    object:文件对象
    LineNumber:文件总行数
@return:假-发送失败，真-发送成功
@note:
****************************************************/
bool HCNG::Protocol_CommandSend_FileTransmit_Finish(quint8 address,quint8 *buf,quint8 object,quint16 LineNumber)
{
    if(buf == NULL)return false;
    if(address >= eDeviceAddress_Master)
    {
        emit MessageHint_signal("设置地址不得大于253或目标地址有误!");return false;
    }
    quint32 pos = 0;

    pos = 0;
    *(buf + pos++) = dFrame_IdentifyA;
    *(buf + pos++) = dFrame_IdentifyA_Invert;
    *(buf + pos++) = 0;
    *(buf + pos++) = 0;

    *(buf + pos++) = eDeviceAddress_Master;//发送设备地址
    *(buf + pos++) = address;//接收设备地址
    *(buf + pos++) = eCommandType_Device;
    *(buf + pos++) = eCodeType_Device_FileTransmit_Finish;

    *(buf + pos++) = object;//文件对象
    *(buf + pos++) = dDataLength_MSB(LineNumber);//文件数据行数
    *(buf + pos++) = dDataLength_LSB(LineNumber);

    *(buf + 2) = dDataLength_MSB(pos-4+1);//帧数据字节数高8位
    *(buf + 3) = dDataLength_LSB(pos-4+1);//帧数据字节数低8位
    *(buf + pos++) = frame_ChecksumGet(buf,pos);
    emit Serial_TransferData_signal(buf,pos);
    return true;
}











